// 安全工具函数
import DOMPurify from 'dompurify'

/**
 * 输入验证工具
 */
export const InputValidator = {
  /**
   * 验证字符串长度
   * @param {string} value - 要验证的值
   * @param {number} min - 最小长度
   * @param {number} max - 最大长度
   * @param {string} fieldName - 字段名称（用于错误消息）
   * @returns {object} 验证结果对象
   */
  validateLength(value, min = 0, max = Infinity, fieldName = '输入') {
    if (typeof value !== 'string') {
      return { valid: false, message: `${fieldName}必须是字符串` }
    }
    
    if (value.length < min) {
      return { valid: false, message: `${fieldName}长度不能少于${min}位` }
    }
    
    if (value.length > max) {
      return { valid: false, message: `${fieldName}长度不能超过${max}位` }
    }
    
    return { valid: true, message: '' }
  },

  /**
   * 验证邮箱格式
   * @param {string} email - 邮箱地址
   * @returns {boolean}
   */
  validateEmail(email) {
    if (typeof email !== 'string') return false
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
    return emailRegex.test(email)
  },

  /**
   * 验证手机号格式
   * @param {string} phone - 手机号
   * @returns {boolean}
   */
  validatePhone(phone) {
    if (typeof phone !== 'string') return false
    const phoneRegex = /^1[3-9]\d{9}$/
    return phoneRegex.test(phone)
  },

  /**
   * 验证密码强度
   * @param {string} password - 密码
   * @returns {object} 验证结果
   */
  validatePassword(password) {
    if (typeof password !== 'string') {
      return { valid: false, strength: 'invalid', errors: ['密码必须是字符串'] }
    }

    const errors = []
    let score = 0

    // 长度检查
    if (password.length < 8) {
      errors.push('密码长度至少8位')
    } else {
      score += 1
    }

    // 包含小写字母
    if (/[a-z]/.test(password)) {
      score += 1
    } else {
      errors.push('密码必须包含小写字母')
    }

    // 包含大写字母
    if (/[A-Z]/.test(password)) {
      score += 1
    } else {
      errors.push('密码必须包含大写字母')
    }

    // 包含数字
    if (/\d/.test(password)) {
      score += 1
    } else {
      errors.push('密码必须包含数字')
    }

    // 包含特殊字符
    if (/[!@#$%^&*(),.?":{}|<>]/.test(password)) {
      score += 1
    }

    let strength = 'weak'
    if (score >= 4) strength = 'strong'
    else if (score >= 3) strength = 'medium'

    return {
      valid: errors.length === 0,
      strength,
      score,
      errors
    }
  },

  /**
   * 清理输入字符串
   * @param {string} input - 输入字符串
   * @returns {string} 清理后的字符串
   */
  sanitizeInput(input) {
    if (typeof input !== 'string') return ''
    
    return input
      .trim()
      .replace(/[<>"'&]/g, (match) => {
        const entities = {
          '<': '&lt;',
          '>': '&gt;',
          '"': '&quot;',
          "'": '&#x27;',
          '&': '&amp;'
        }
        return entities[match]
      })
  }
}

/**
 * XSS防护工具
 */
export const XSSProtection = {
  /**
   * 检测XSS攻击
   * @param {string} input - 输入字符串
   * @returns {boolean} 是否包含XSS攻击代码
   */
  detectXSS(input) {
    if (typeof input !== 'string') return false
    
    // XSS攻击模式检测
    const xssPatterns = [
      /<script[^>]*>.*?<\/script>/gi,
      /<iframe[^>]*>.*?<\/iframe>/gi,
      /<object[^>]*>.*?<\/object>/gi,
      /<embed[^>]*>/gi,
      /<link[^>]*>/gi,
      /<meta[^>]*>/gi,
      /javascript:/gi,
      /vbscript:/gi,
      /data:text\/html/gi,
      /on\w+\s*=/gi, // 事件处理器
      /<\s*\w*\s+on\w+/gi,
      /expression\s*\(/gi,
      /@import/gi,
      /url\s*\(/gi
    ]
    
    return xssPatterns.some(pattern => pattern.test(input))
  },

  /**
   * 清理输入内容
   * @param {string} input - 输入字符串
   * @param {object} options - 清理选项
   * @returns {string} 清理后的字符串
   */
  sanitizeInput(input, options = {}) {
    if (typeof input !== 'string') return ''
    
    const defaultOptions = {
      allowHtml: false,
      maxLength: 1000,
      trimWhitespace: true
    }
    
    const config = { ...defaultOptions, ...options }
    
    let sanitized = input
    
    // 限制长度
    if (config.maxLength && sanitized.length > config.maxLength) {
      sanitized = sanitized.substring(0, config.maxLength)
    }
    
    // 清理空白字符
    if (config.trimWhitespace) {
      sanitized = sanitized.trim()
    }
    
    // HTML转义
    if (!config.allowHtml) {
      sanitized = this.escapeHtml(sanitized)
    } else {
      sanitized = this.sanitizeHtml(sanitized)
    }
    
    return sanitized
  },

  /**
   * HTML转义
   * @param {string} html - HTML字符串
   * @returns {string} 转义后的字符串
   */
  escapeHtml(html) {
    if (typeof html !== 'string') return ''
    
    const escapeMap = {
      '&': '&amp;',
      '<': '&lt;',
      '>': '&gt;',
      '"': '&quot;',
      "'": '&#x27;'
    }
    
    return html.replace(/[&<>"']/g, (match) => escapeMap[match])
  },

  /**
   * 清理HTML内容
   * @param {string} html - HTML字符串
   * @param {object} options - DOMPurify选项
   * @returns {string} 清理后的HTML
   */
  sanitizeHtml(html, options = {}) {
    if (typeof html !== 'string') return ''
    
    const defaultOptions = {
      ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'u', 'ol', 'ul', 'li'],
      ALLOWED_ATTR: ['class'],
      FORBID_SCRIPT: true,
      FORBID_TAGS: ['script', 'object', 'embed', 'link', 'style', 'img'],
      FORBID_ATTR: ['onerror', 'onload', 'onclick', 'onmouseover']
    }
    
    return DOMPurify.sanitize(html, { ...defaultOptions, ...options })
  },

  /**
   * 清理URL
   * @param {string} url - URL字符串
   * @returns {string} 清理后的URL
   */
  sanitizeUrl(url) {
    if (typeof url !== 'string') return 'about:blank'
    
    // 检查危险协议
    const dangerousProtocols = ['javascript:', 'data:', 'vbscript:']
    const lowerUrl = url.toLowerCase()
    
    for (const protocol of dangerousProtocols) {
      if (lowerUrl.startsWith(protocol)) {
        return 'about:blank'
      }
    }
    
    // 允许相对路径和锚点
    if (url.startsWith('/') || url.startsWith('#')) {
      return url
    }
    
    try {
      const urlObj = new URL(url)
      
      // 只允许http和https协议
      if (!['http:', 'https:'].includes(urlObj.protocol)) {
        return 'about:blank'
      }
      
      return urlObj.toString()
    } catch {
      return 'about:blank'
    }
  },

  /**
   * 验证CSP策略
   * @param {string} csp - CSP策略字符串
   * @returns {boolean} 是否有效
   */
  validateCSP(csp) {
    if (typeof csp !== 'string') return false
    
    // 基本的CSP验证
    const validDirectives = [
      'default-src', 'script-src', 'style-src', 'img-src', 'connect-src',
      'font-src', 'object-src', 'media-src', 'frame-src', 'sandbox',
      'report-uri', 'child-src', 'form-action', 'frame-ancestors',
      'plugin-types', 'base-uri', 'report-to', 'worker-src', 'manifest-src'
    ]
    
    const directives = csp.split(';').map(d => d.trim())
    
    for (const directive of directives) {
      if (!directive) continue
      
      const [name] = directive.split(' ')
      if (!validDirectives.includes(name)) {
        return false
      }
    }
    
    return true
  }
}

/**
 * CSRF防护工具
 */
export const CSRFProtection = {
  /**
   * 生成CSRF令牌
   * @returns {string} CSRF令牌
   */
  generateToken() {
    const array = new Uint8Array(32)
    crypto.getRandomValues(array)
    return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('')
  },

  /**
   * 验证CSRF令牌
   * @param {string} token - 要验证的令牌
   * @param {string} storedToken - 存储的令牌
   * @returns {boolean} 是否有效
   */
  validateToken(token, storedToken) {
    if (typeof token !== 'string' || typeof storedToken !== 'string') {
      return false
    }
    
    if (token.length !== storedToken.length) {
      return false
    }
    
    // 使用时间安全的比较
    let result = 0
    for (let i = 0; i < token.length; i++) {
      result |= token.charCodeAt(i) ^ storedToken.charCodeAt(i)
    }
    
    return result === 0
  },

  /**
   * 获取存储的令牌
   * @returns {string|null} 令牌或null
   */
  getToken() {
    if (typeof window === 'undefined' || !window.sessionStorage) {
      return null
    }
    
    return window.sessionStorage.getItem('csrf_token')
  },

  /**
   * 设置令牌
   * @param {string} token - 令牌
   */
  setToken(token) {
    if (typeof window === 'undefined' || !window.sessionStorage) {
      return
    }
    
    window.sessionStorage.setItem('csrf_token', token)
  },

  /**
   * 清除令牌
   */
  clearToken() {
    if (typeof window === 'undefined' || !window.sessionStorage) {
      return
    }
    
    window.sessionStorage.removeItem('csrf_token')
  }
}

/**
 * 安全配置
 */
export const SecurityConfig = {
  // CSP策略
  csp: {
    'default-src': ["'self'"],
    'script-src': ["'self'", "'unsafe-inline'"],
    'style-src': ["'self'", "'unsafe-inline'"],
    'img-src': ["'self'", 'data:', 'https:'],
    'connect-src': ["'self'"],
    'font-src': ["'self'"],
    'object-src': ["'none'"],
    'media-src': ["'self'"],
    'frame-src': ["'none'"]
  },
  
  // 安全头部
  headers: {
    'X-Content-Type-Options': 'nosniff',
    'X-Frame-Options': 'DENY',
    'X-XSS-Protection': '1; mode=block',
    'Referrer-Policy': 'strict-origin-when-cross-origin',
    'Permissions-Policy': 'camera=(), microphone=(), geolocation=()'
  },
  
  // 输入验证规则
  validation: {
    maxInputLength: 1000,
    maxFileSize: 10 * 1024 * 1024, // 10MB
    allowedFileTypes: ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
    passwordMinLength: 8,
    passwordMaxLength: 128
  }
}

/**
 * 安全工具集合
 */
export const SecurityUtils = {
  InputValidator,
  XSSProtection,
  CSRFProtection,
  SecurityConfig,
  
  /**
   * 防抖函数
   * @param {Function} func - 要防抖的函数
   * @param {number} wait - 等待时间（毫秒）
   * @param {boolean} immediate - 是否立即执行
   * @returns {Function} 防抖后的函数
   */
  debounce(func, wait, immediate = false) {
    let timeout
    return function executedFunction(...args) {
      const later = () => {
        timeout = null
        if (!immediate) func.apply(this, args)
      }
      const callNow = immediate && !timeout
      clearTimeout(timeout)
      timeout = setTimeout(later, wait)
      if (callNow) func.apply(this, args)
    }
  },
  
  /**
   * 初始化安全设置
   */
  init() {
    // 设置CSP
    this.setCSP()
    
    // 生成CSRF令牌
    if (!CSRFProtection.getToken()) {
      CSRFProtection.setToken(CSRFProtection.generateToken())
    }
  },
  
  /**
   * 设置CSP策略
   */
  setCSP() {
    if (typeof document === 'undefined') return
    
    const cspString = Object.entries(SecurityConfig.csp)
      .map(([directive, sources]) => `${directive} ${sources.join(' ')}`)
      .join('; ')
    
    const meta = document.createElement('meta')
    meta.httpEquiv = 'Content-Security-Policy'
    meta.content = cspString
    
    document.head.appendChild(meta)
  }
}

export default SecurityUtils